package de.dnb.gnd.utils;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;

import de.dnb.basics.applicationComponents.Pair;
import de.dnb.basics.applicationComponents.RangeCheckUtils;
import de.dnb.gnd.exceptions.ExceptionHandler;
import de.dnb.gnd.exceptions.WrappingHandler;
import de.dnb.gnd.parser.Indicator;
import de.dnb.gnd.parser.Record;
import de.dnb.gnd.parser.line.Line;
import de.dnb.gnd.parser.tag.BibTagDB;
import de.dnb.gnd.parser.tag.Tag;
import de.dnb.gnd.parser.tag.TagDB;

public final class BibRecUtils {

	private BibRecUtils() {
	}

	/**
	 * Sichert zu, dass record nicht null ist und mithilfe der
	 * GND-Tag-Datenbank aufgebaut worden ist (also kein
	 * Titeldatensatz ist). Andernfalls wird eine 
	 * {@link IllegalArgumentException} geworfen.
	 * 
	 * 
	 * @param record	nicht null
	 */
	public static void assertBibRecord(final Record record) {
		RangeCheckUtils.assertReferenceParamNotNull("record", record);
		if (!RecordUtils.isBibliographic(record)) {
			throw new IllegalArgumentException("Datensatz mit idn "
				+ record.getId() + "\n" + record.getRawData()
				+ "\nist kein Titeldatensatz");
		}
	}

	private static final BibTagDB TAG_DB = BibTagDB.getDB();

	/**
	 * Liest einen Datensatz von der Standardeingabe. Nimmt an, dass Titeldaten
	 * vorliegen. Gibt Fehler aus.
	 * 
	 * @return neuen Datensatz.
	 */
	public static Record readFromConsole() {
		return RecordUtils.readFromConsole(TAG_DB, new WrappingHandler());
	}

	/**
	 * Liest einen Datensatz aus der Zwischenablage. Fehler werden
	 * ignoriert.
	 * @return		neuen Datensatz oder null.
	 */
	public static Record readFromClipIgnoring() {
		return RecordUtils.readFromClip(TAG_DB);
	}

	/**
	 * Liest einen Datensatz aus der Zwischenablage. Gibt Fehler aus.
	 * @return		neuen Datensatz oder null.
	 */
	public static Record readFromClip() {
		return RecordUtils.readFromClip(TAG_DB, new WrappingHandler(),
			false);
	}

	/**
	 * Liest einen Datensatz aus der Zwischenablage.
	 * @param handler	beliebig
	 * @return			neuen Datensatz oder null.
	 */
	public static Record readFromClip(final ExceptionHandler handler) {
		return RecordUtils.readFromClip(TAG_DB, handler, false);
	}

	/**
	 * Liest einen Datensatz von der Standardeingabe. Nimmt an, dass Titeldaten
	 * vorliegen. Ignoriert Fehler.
	 * 
	 * @return neuen Datensatz.
	 */
	public static Record readFromConsoleIgnoring() {
		return RecordUtils.readFromConsole(TAG_DB);
	}

	public static boolean hasIgnorableIndicator(Record record) {
		assertBibRecord(record);
		for (Line line : record) {
			Tag tag = line.getTag();
			if (!tag.hasIgnorableIndicator())
				continue;
			Indicator indicator = tag.getIgnorableIndicator();
			if (SubfieldUtils.containsIndicator(line, indicator))
				return true;
		}
		return false;
	}

	/**
	 * Gibt das Anfangssegment.
	 * 
	 * @param record  	nicht null
	 * @return			nicht null; neue, modifizierbare Liste	
	 */
	public static ArrayList<Line> getHeadersSegment(final Record record) {
		assertBibRecord(record);
		return RecordUtils.getLines(record, TAG_DB.getHeaders());
	}

	/**
	 * Segment Personennamen.
	 * 
	 * @param record  	nicht null
	 * @return			nicht null; neue, modifizierbare Liste	
	 */
	public static ArrayList<Line> getPersonalNameSegment(
		final Record record) {
		assertBibRecord(record);
		return RecordUtils.getLines(record,
			TAG_DB.getPersonalNameSegment());
	}

	/**
	 * Segment Körperschaftsnamen.
	 * 
	 * @param record  	nicht null
	 * @return			nicht null; neue, modifizierbare Liste	
	 */
	public static ArrayList<Line> getCoporateNameSegment(
		final Record record) {
		assertBibRecord(record);
		return RecordUtils.getLines(record,
			TAG_DB.getCoporateNameSegment());
	}

	/**
	 * Segment Sachtitel.
	 * 
	 * @param record  	nicht null
	 * @return			nicht null; neue, modifizierbare Liste	
	 */
	public static ArrayList<Line> getTitleSegment(final Record record) {
		assertBibRecord(record);
		return RecordUtils.getLines(record, TAG_DB.getTitleSegment());
	}

	/**
	 * Segment Segment Veröff.-Vermerk, Umfang, Beilagen.
	 * 
	 * @param record  	nicht null
	 * @return			nicht null; neue, modifizierbare Liste	
	 */
	public static ArrayList<Line> getEditionSegment(final Record record) {
		assertBibRecord(record);
		return RecordUtils.getLines(record, TAG_DB.getEditionSegment());
	}

	/**
	 * Segment Übergeordnete Gesamtheiten / Sekundärausgaben.
	 * 
	 * @param record  	nicht null
	 * @return			nicht null; neue, modifizierbare Liste	
	 */
	public static ArrayList<Line> getSeriesStatementSegment(
		final Record record) {
		assertBibRecord(record);
		return RecordUtils.getLines(record,
			TAG_DB.getSeriesStatementSegment());
	}

	/**
	 * Segment Fußnoten.
	 * 
	 * @param record  	nicht null
	 * @return			nicht null; neue, modifizierbare Liste	
	 */
	public static ArrayList<Line> getNoteSegment(final Record record) {
		assertBibRecord(record);
		return RecordUtils.getLines(record, TAG_DB.getNoteSegment());
	}

	/**
	 * Segment Titelverknüpfungen.
	 * 
	 * @param record  	nicht null
	 * @return			nicht null; neue, modifizierbare Liste	
	 */
	public static ArrayList<Line> getTitleLinkSegment(final Record record) {
		assertBibRecord(record);
		return RecordUtils.getLines(record, TAG_DB.getTitleLinkSegment());
	}

	/**
	 * Segment Feldgruppen für Nicht-Standard-NEE.
	 * 
	 * @param record  	nicht null
	 * @return			nicht null; neue, modifizierbare Liste	
	 */
	public static ArrayList<Line> getAddedEntryFieldsSegment(
		final Record record) {
		assertBibRecord(record);
		return RecordUtils.getLines(record,
			TAG_DB.getAddedEntryFieldsSegment());
	}

	/**
	 * Segment Sonstige Angaben.
	 * 
	 * @param record  	nicht null
	 * @return			nicht null; neue, modifizierbare Liste	
	 */
	public static ArrayList<Line> getOtherInformationSegment(
		final Record record) {
		assertBibRecord(record);
		return RecordUtils.getLines(record,
			TAG_DB.getOtherInformationSegment());
	}

	/**
	 * Segment Bearbeiterzeichen.
	 * 
	 * @param record  	nicht null
	 * @return			nicht null; neue, modifizierbare Liste	
	 */
	public static ArrayList<Line> getResponsibilitySegment(
		final Record record) {
		assertBibRecord(record);
		return RecordUtils.getLines(record,
			TAG_DB.getResponsibilitySegment());
	}

	/**
	 * Segment Sacherschließung.
	 * 
	 * @param record  	nicht null
	 * @return			nicht null; neue, modifizierbare Liste	
	 */
	public static ArrayList<Line> getSubjectAccessSegment(
		final Record record) {
		assertBibRecord(record);
		return RecordUtils.getLines(record,
			TAG_DB.getSubjectAccessSegment());
	}

	/**
	 * Segment Exemplardaten.
	 * 
	 * @param record  	nicht null
	 * @return			nicht null; neue, modifizierbare Liste	
	 */
	public static ArrayList<Line> getHoldingsSegment(final Record record) {
		assertBibRecord(record);
		return RecordUtils.getLines(record, TAG_DB.getHoldingsSegment());
	}

	/**
	 * Entfernt Exemplardaten.
	 * 
	 * @param record  	nicht null
	 * 
	 */
	public static void removeHoldingsSegment(final Record record) {
		assertBibRecord(record);
		RecordUtils.removeTags(record, TAG_DB.getHoldingsSegment());
	}

	public static String getMainTitle(Record record) {
		Line titleLine = getMainTitleLine(record);
		if (titleLine == null)
			return null;
		else {
			return SubfieldUtils.getContentOfFirstSubfield(titleLine, 'a');
		}
	}

	public static Line getMainTitleLine(Record record) {
		return RecordUtils.getTheOnlyLine(record, "4000");
	}

	/**
	 * Macht aus Titeldaten einen Kurztitel.
	 * 
	 * @param record	nicht null.
	 * @return			nicht null?
	 */
	public static String createShortTitle(final Record record) {
		assertBibRecord(record);
		String title;
		String creator;

		title = RecordUtils.getContentOfSubfield(record, "4000", 'a');
		// wohl Af:
		if (title == null) {
			String unselbständig =
				RecordUtils.getContentOfSubfield(record, "4004", 'a');
			title = RecordUtils.getContentOfSubfield(record, "4000", '8');
			if (title != null) {
				if (unselbständig != null)
					title = title + "; " + unselbständig;
			} else
				// titel immer noch null, daher letzte Rettung
				title = unselbständig;
		}

		creator = RecordUtils.getContentOfSubfield(record, "3000", '8');
		if (creator == null)
			creator =
				RecordUtils.getContentOfSubfield(record, "3100", '8');
		//		if (creator == null)
		//			creator = RecordUtils.getContentOfSubfield(record, "3010", '8');
		//		if (creator == null) {
		//			creator = RecordUtils.getContentOfSubfield(record, "3120", '8');
		//		}

		if (creator != null) {
			if (creator.contains("$l"))
				creator = creator.replace("$l", " <") + ">";
			if (creator.contains("$g"))
				creator = creator.replace("$g", " <") + ">";
			if (creator.contains("$b")) {
				// $gKiel$bBibliothek -> <Kiel, Bibliothek>:
				if (creator.contains("<"))
					creator = creator.replace("$b", ", ");
				else
					creator = creator.replace("$b", " <") + ">";
			}
			creator = creator.replace("$c", " ");
			title = creator + " : " + title;
			// hier darf man @ entfernen, da der Autor (hoffentlich) keine
			// Artikel ... enthält und an erster Stelle steht:
			title = title.replace("@", "");
		}
		// Für anonyme Werke kein @ entfernen, da Excel falsch sortiert und
		// dann die betreffenden Titel nicht händisch gefunden werden:
		return title;
	}

	/**
	 * 
	 * @param record nicht null
	 * @return			Sprachen des Textes aus Feld 1500 /1 .. /1 .. 
	 * 					nicht null, modifizierbare Liste
	 */
	public static List<String> getLanguagesOfText(final Record record) {
		RangeCheckUtils.assertReferenceParamNotNull("record", record);
		Line line = RecordUtils.getTheOnlyLine(record, "1500");
		if (line == null)
			return Collections.emptyList();
		else
			return SubfieldUtils.getContentsOfSubfields(line, 'a');
	}

	/**
	 * 
	 * @param record nicht null
	 * @return			vierstelliges Erscheinungsjahr aus Feld 1100 $a 
	 * 					oder null
	 */
	public static String getDateOfPublication(final Record record) {
		RangeCheckUtils.assertReferenceParamNotNull("record", record);
		Line line = RecordUtils.getTheOnlyLine(record, "1100");
		if (line == null)
			return null;
		else
			return SubfieldUtils.getContentOfFirstSubfield(line, 'a');
	}

	/**
	 * 
	 * @param record nicht null
	 * @return			Lieferungsnummern (Feld ist wiederholbar) der 
	 * 					Deutschen Nationalbibliografie in der Form 
	 * 					<br><code>JJ,RHH(,ZZZZ)</code><br> 
	 * 					nicht null
	 */
	public static Collection<String> getWVNumbers(final Record record) {
		RangeCheckUtils.assertReferenceParamNotNull("record", record);
		return RecordUtils
			.getContentsOfFirstSubfields(record, "2105", '0');
	}

	/**
	 * Liefert aus Feld 2105 eine Liste der Anzeigejahre und der Anzeigereihen.
	 * Das Feld sieht in der Regel so aus:<br><br>
	 * 
	 * 2105 98,A39,0360
	 * 
	 * <br><br>
	 * Daraus wird ein Paar <98, A> erzeugt.
	 * 
	 * @param record	nicht null
	 * @return			Anzeigejahre und  Anzeigereihen in der Form 
	 * 					<br><code><15, A>, <15, H></code><br>
	 * 					nicht null
	 */
	public static Collection<Pair<String, String>> getYearAndSeries(
		final Record record) {
		RangeCheckUtils.assertReferenceParamNotNull("record", record);
		Collection<String> wvs = getWVNumbers(record);
		Collection<Pair<String, String>> pairs = new LinkedList<>();
		for (String wv : wvs) {
			String[] wvFractions = wv.split("\\,");
			if (wvFractions.length < 2)
				continue;
			String year = wvFractions[0];
			String reihe = wvFractions[1];
			if (reihe.length() < 1)
				continue;
			Pair<String, String> pair =
				new Pair<String, String>(year, reihe.substring(0, 1));
			pairs.add(pair);
		}
		return pairs;
	}

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		Record record = readFromClip();
		System.out.println(getYearAndSeries(record));
	}
}
